#include <unordered_map>
#include <cassert>
#include <iostream>

#include "Unit.hpp"
#include "UnitManager.hpp"
#include "UnitStats.hpp"
#include "Inventory.hpp"

#include "Debug.hpp"

using namespace std;

CUnitManager::CUnitManager() : nextUnitId(0)
{

}

CUnitManager::~CUnitManager()
{
	auto begin = playerMap.begin();
	auto end = playerMap.end();
	Unit<UnitPlayer>* unit = nullptr;
	for(; begin != end;)
	{
		unit = begin->second;
		begin = playerMap.erase(begin);
		delete unit;
	}
}

Unit<UnitPlayer>* CUnitManager::InitPlayer(int32_t playerLevel, PlayerClass playerClass, PlayerSex playerSex)
{

	auto player = new Unit<UnitPlayer>;

	player->playerClass = playerClass;
	player->playerSex = playerSex;
	player->playerLevel = playerLevel;

	return player;
}

Unit<UnitMonster>* CUnitManager::InitMonster(int32_t level)
{
	auto monster = new Unit<UnitMonster>;

	//monster->monsterClass = monsterClass;
	//monster->Sex = sex;
	monster->monsterLevel = level;

	return monster;
}

Unit<UnitMissile>* CUnitManager::InitMissile(int32_t level)
{
	auto missile = new Unit<UnitMissile>;

	//missile->missileClass = playerClass;
	missile->missileLevel = level;

	return missile;
}

Unit<UnitItem>* CUnitManager::InitItem(int32_t level, ItemClass itemClass)
{
	auto item = new Unit<UnitItem>;

	item->itemClass = itemClass;
	item->itemLevel = level;
	item->ownerId = -1;

	return item;
}

Unit<UnitObject>* CUnitManager::InitObject()
{
	auto object = new Unit<UnitObject>;

	//object->objectClass = playerClass;
	//object->objectLevel = level;

	return object;
}

Unit<UnitPlayer>* CUnitManager::CreatePlayer(string playerName, PlayerClass playerClass, PlayerSex playerSex)
{
	auto player = InitPlayer(1, playerClass, playerSex);

	player->unitName = playerName;

	CreateUnitStat(player, StatId::STAT_STR, initialStats[(int32_t)playerClass].str);
	CreateUnitStat(player, StatId::STAT_DEX, initialStats[(int32_t)playerClass].dex);
	CreateUnitStat(player, StatId::STAT_VITA, initialStats[(int32_t)playerClass].vita);
	CreateUnitStat(player, StatId::STAT_ENER, initialStats[(int32_t)playerClass].ener);
	CreateUnitStat(player, StatId::STAT_HEALTHPERLEVEL, initialStats[(int32_t)playerClass].healthPerLevel);
	CreateUnitStat(player, StatId::STAT_MANAPERLEVEL, initialStats[(int32_t)playerClass].manaPerLevel);
	CreateUnitStat(player, StatId::STAT_STAMINAPERLEVEL, initialStats[(int32_t)playerClass].staminaPerLevel);
	CreateUnitStat(player, StatId::STAT_MAXHEALTH, initialStats[(int32_t)playerClass].healthPerLevel * player->playerLevel);
	CreateUnitStat(player, StatId::STAT_MAXMANA, initialStats[(int32_t)playerClass].manaPerLevel * player->playerLevel);
	CreateUnitStat(player, StatId::STAT_MAXSTAMINA, initialStats[(int32_t)playerClass].staminaPerLevel * player->playerLevel);
	CreateUnitStat(player, StatId::STAT_HEALTH, CalcStat<UnitPlayer>(player, StatId::STAT_MAXHEALTH));
	CreateUnitStat(player, StatId::STAT_MANA, CalcStat<UnitPlayer>(player, StatId::STAT_MAXMANA));
	CreateUnitStat(player, StatId::STAT_STAMINA, CalcStat<UnitPlayer>(player, StatId::STAT_MAXSTAMINA));

	// Add the player to the unit map
	if(!AddLink<UnitPlayer>(player))
	{
		delete player;
		throw;
	}
	return player;
}


bool CUnitManager::UpdateUnits(void)
{
	while(objectUpdates.size())
	{
		auto unit = objectUpdates.top();
		objectUpdates.pop();
	}

	while(missileUpdates.size())
	{
		auto unit = missileUpdates.top();
		missileUpdates.pop();
	}

	while(itemUpdates.size())
	{
		auto unit = itemUpdates.top();
		itemUpdates.pop();
	}

	while(monsterUpdates.size())
	{
		auto unit = monsterUpdates.top();
		monsterUpdates.pop();
	}

	while(playerUpdates.size())
	{
		auto unit = playerUpdates.top();

#if 0
		int32_t health = 0, mana = 0, stamina = 0, 
				healthmax = 0, manamax = 0, staminamax = 0;
		health = statManager.CalcStat(*unit, STAT_HEALTH);
		mana = statManager.CalcStat(*unit, STAT_MANA);
		stamina = statManager.CalcStat(*unit, STAT_STAMINA);
		healthmax = statManager.CalcStat(*unit, STAT_MAXHEALTH);
		manamax = statManager.CalcStat(*unit, STAT_MAXMANA);
		staminamax = statManager.CalcStat(*unit, STAT_MAXSTAMINA);

		int32_t level = 0;

		cout << "level " << level << 
				" class " << unit->unitType <<
				//" health per level " << initialStats[unit->playerClass].healthPerLevel <<
				" health " << health << 
				" mana " << mana << 
				" stamina " << stamina << 
				" max health " << healthmax << 
				" max mana " << manamax << 
				" max stamina " << staminamax << 
				endl;
#endif

		playerUpdates.pop();
	}
	return true;
}
